var fs = require('fs');

var mineQuantityPos = {
    1 : 13,
    2 : 17,
    3 : 21,
    4 : 21,
    5 : 21,
    6 : 21,
    7 : 21,
};

function getMineGroup(serverId){
    return Math.ceil(serverId/50);
}

function getMineQuantity(id) {
    return (id > 3 ? 3 : id);
}

function ServerMine(){
    this.occupying = {};
    this.users = {};
    this.mines = {};
    this.leaves = {};/* uid : {
                            {gold:, enemy:, time:}
                        }
                    */
    this.dirtyCount = 0;
    this.updates = {};

    this.messages = {}; //消息
}

ServerMine.create = function() {
    gDBWorld.insert({_id:'server_mine', mine:{}, leave:{}}, function(err, result){}); 
};

ServerMine.prototype = {
    init : function(callback) {

        // 读取金矿数据
        gDBWorld.findOne({_id : 'server_mine'}, {}, function(err, doc){
            if( doc ) {
                this.mines = doc.mine;
                this.leaves = doc.leave;
                this._init();
                callback && callback(true);
            }else{
                callback && callback(false);
            }
        }.bind(this));
        
        // 读取玩家数据
        if( fs.existsSync('log/servermine.json') ) {
            var users = null;
            try {
                users = JSON.parse(fs.readFileSync('log/servermine.json', 'utf8'));
            }catch(e){
            }

            users = users || {};
            for( var uid in users ) {
                var item = users[uid];
                var player = new Player(item._id);
                player.initWithUniverseData(item);
                this.users[item._id] = player;
            }
        }
        INFO('server_mine user loaded');
    },

    _init : function() {
        for( var group in this.mines ) {
            var gmines = this.mines[group];
            for( var id in gmines ) {
                var mines = gmines[id];
                for( var pos in mines ) {
                    var mine = mines[pos];
                    if( mine.owner ) {
                        this.occupying[mine.owner] = {id:id, pos:pos};
                    }
                }
            }
        }
    },

    getPageMines : function(group, page) {
        if (!(page in mineQuantityPos)) {
            return null;
        }

        var groupMines = this.mines[group];
        if (!groupMines) {
            groupMines = {};
            this.mines[group] = groupMines;
        }

        var pageMines = groupMines[page];
        if (!pageMines) {
            pageMines = {};
            groupMines[page] = pageMines;

            var posCount = mineQuantityPos[page];
            for (var pos=1; pos<=posCount; ++pos) {
                var mine = {
                    owner: 0,   // 占领者ID(没有为0)
                    timer: 0,   // 占领时间
                    output: 0,  // 赔偿产量
                    name: '',
                    headpic: '',
                    league: '',
                };
                pageMines[pos] = mine;
                this.markMineDirty(group, page, pos, mine);
            }
        } else {
            var now = common.getTime();
            var maxTime = gConfGlobal.ServerMineOccupyTime * 3600;
            var posArr = Object.keys(pageMines);
            for (var i = 0, len = posArr.length; i < len; ++i) {
                var pos = posArr[i];
                this._checkMine(group, page, pos, pageMines[pos], maxTime, now);
            }
        }

        this.save();
        return pageMines;
    },
    
    //检测矿区是否到占矿结束
    _checkMine: function(group, page, pos, mine, maxTime, now) {
        if (!mine || !mine.owner || (mine.timer + maxTime) > now) {
            return false;
        }
        
        // 时间到了,自动离开
        var userInfo = this.users[mine.owner];
        if (userInfo) {
            var output = this.getOutput(group, page, pos, userInfo.user.status.level);
            this.addLeave(mine.owner, {gold:output, enemy:0, time:now});
        } else {
            this.removeUser(mine.owner);
        }
        
        mine.owner = 0;
        mine.timer = 0;
        mine.output = 0;
        mine.name = '';
        mine.headpic = '';
        mine.league = '';

        this.markMineDirty(group, page, pos, mine);
        return true;
    },

    checkUserMine: function(group, uid) {
        var occupying = this.occupying[uid];
        if (occupying) {
            var invalid = false;
            var id = occupying.id;
            var groupMines = this.mines[group];
            if (groupMines) {
                var pageMines = groupMines[occupying.id];
                if (pageMines) {
                    var mine = pageMines[occupying.pos];
                    if (mine && (mine.owner == uid)) {
                        var now = common.getTime();
                        var maxTime = gConfGlobal.ServerMineOccupyTime * 3600;
                        if (this._checkMine(group, occupying.id, occupying.pos, mine, maxTime, now)) {
                            this.save();
                        }
                    } else {
                        invalid = true;
                    }
                } else {
                    invalid = true;
                }
            } else {
                invalid = true;
            }

            if (invalid) {
                delete this.occupying[uid];
            }
        }
        return this.occupying[uid];
    },

    getOutput : function(group, id, pos, userLevel){
        var mine = this.mines[group][id][pos];
        var quantity = getMineQuantity(id);
        var mineConf = gConfServerMine[quantity];

        var outputPerHour = mineConf.OutputPerHour * gConfLevel[userLevel].ServerMineOutputFactor;
        var outputTime = Math.min(gConfGlobal.ServerMineOccupyTime*3600, common.getTime() - mine.timer);

        var allOutput = mine.output + Math.floor(outputTime/3600 * outputPerHour);
        var maxOutput = Math.floor(gConfGlobal.ServerMineMaxOutput * outputPerHour);
       
        return Math.min(allOutput, maxOutput);
    },

    isFull : function(group, id, pos, userLevel) {
        var mine = this.mines[group][id][pos];
        var quantity = getMineQuantity(id);
        var mineConf = gConfServerMine[quantity];

        var outputPerHour = mineConf.OutputPerHour * gConfLevel[userLevel].ServerMineOutputFactor;
        var outputTime = Math.min(gConfGlobal.ServerMineOccupyTime*3600, common.getTime() - mine.timer);

        var allOutput = mine.output + Math.floor(outputTime/3600 * outputPerHour);
        var maxOutput = Math.floor(gConfGlobal.ServerMineMaxOutput * outputPerHour);
        
        return (allOutput >= maxOutput) ? true : false;
    },

    getWarCost : function(group, id, pos) {
        var mine = this.mines[group][id][pos];
        var quantity = getMineQuantity(id);
        var mineConf = gConfServerMine[quantity];

        var outputPerHour = mineConf.OutputPerHour * gConfGlobal.ServerMineRobFactor;
        var outputTime = Math.min(gConfGlobal.ServerMineOccupyTime*3600, common.getTime() - mine.timer);

        var allOutput = mine.output + Math.floor(outputTime/3600 * outputPerHour);
        var maxOutput = Math.floor(outputPerHour * gConfGlobal.ServerMineMaxOutput);
        
        return Math.floor(Math.min(allOutput, maxOutput)*mineConf.Cost/100);
    },

    markMineDirty : function(group, id, pos, mine){
        this.updates[util.format('mine.%d.%d.%d', group, id, pos)] = mine;
        this.dirtyCount += 1;
    },

    save : function(force, callback) {
        if( !force && (this.dirtyCount < 10) ) {
            callback && callback(true);
            return;
        }

        var updates = this.updates;
        this.updates = {};
        this.dirtyCount = 0;

        gDBWorld.update({_id : 'server_mine'}, {$set:updates}, function(err, result){
            if( err ) {
                ERROR(util.format('SAVE MINE: %j %j', updates, err));
                callback && callback(false);
            }else {
                LOG(util.format('SAVE MINE: %j', updates));
                callback && callback(true);
            }
        });
    },

    addLeave : function(uid, info) {
        this.leaves[uid] = info;
        this.saveLeave(uid);
        this.removeUser(uid);
    },

    saveLeave : function(uid) {
        this.updates[util.format('leave.%d', uid)] = this.leaves[uid] || 0;
        this.dirtyCount += 1;
    },

    getLeave : function(uid) {
        var leave = this.leaves[uid] || 0;
        if( leave ) {
            this.leaves[uid] = 0;
            this.saveLeave(uid);
        }

        return leave;
    },

    updateUser : function(user) {
        var uid = user._id;
        var player = new Player(uid);
        player.initWithUniverseData(user);
        this.users[uid] = player;
    },

    removeUser : function(uid) {
        delete this.occupying[uid];
        delete this.users[uid];
    },

    saveUser : function(uid) {
        var users = {};
        for( var uid in this.users ) {
            users[uid] = this.users[uid].user;
        }
        
        fs.writeFileSync('log/servermine.json', JSON.stringify(users));
    },

    addMessage : function(uid, message) {
        if( !this.messages[uid] ) {
            this.messages[uid] = [];
        }
        this.messages[uid].push(message);
    },

    getMessage : function(uid) {
        var messages = this.messages[uid] || [];
        this.messages[uid] = [];
        
        return messages;
    },

};

exports.get = function(req, res, resp) {
    resp.nolog = 1;
    do {
        var uid = req.uid; 
        var id = 1;
        var group = getMineGroup(req.args.server);

        var occupying = gServerMine.checkUserMine(group, uid);
        if( !isNaN(req.args.id) ) {
            // 读取指定金矿
            id = +req.args.id;
        }else if( occupying ) {
            // 读取自己占领金矿
            id = occupying.id;
        }

        var mines = gServerMine.getPageMines(group, id);
        if (!mines) {
            resp.code = 1; resp.desc = 'id not exist in mine'; break;
        }

        resp.data.mines = mines;
        resp.data.leave = gServerMine.getLeave(uid);
        resp.data.occupying = occupying;

    }while(false);

    onReqHandled(res, resp);
}

exports.occupy = function(req, res, resp) {
    do {
        var uid = req.uid;
        var group = getMineGroup(req.args.server);
        var id = req.args.id;
        var pos = req.args.pos;
        var doRob = req.args.occupy ? false : true;
        var haveGold = req.args.havegold;
        var user = req.args.user;

        if( gServerMine.occupying[uid] ) {
            resp.code = 1; resp.desc = 'occupying'; break;
        }

        var mines = gServerMine.getPageMines(group, id);
        if (!mines) {
            resp.code = 1; resp.desc = 'id not exist in mine'; break;
        }

        var mine = mines[pos];
        if (!mine) {
            resp.code = 1; resp.desc = 'pos not in mine'; break;
        }

        if( doRob && !mine.owner ) {
            // 抢劫,但是没有人占领
            resp.data.already = 1; 
            break;
        }

        if( !doRob && mine.owner ) {
            // 占领,但是已经有人
            resp.data.already = 1; 
            break;
        }

        if( doRob && mine.owner == uid ) {
            resp.data.already = 1; 
            break;
        }

        var name = user.info.un;
        var headpic = user.info.headpic;
        var userLevel = user.status.level;
        gServerMine.updateUser(user);

        var sheldTime = 58;
        if( mine.owner && (common.getTime() - mine.timer) < sheldTime ) {
            resp.code = 1; resp.desc = 'mine in sheld'; break;
        }

        var mineInfo = {group: group, id: id, pos: pos};
        var occupied = true;
        var attackTime = common.getTime();     

        var enemyUid = mine.owner;
        var enemyPlayer = gServerMine.users[mine.owner];
        if (doRob && enemyPlayer) {
            // 掠夺
            var attackerPlayer = gServerMine.users[uid];
            var currentOutput = gServerMine.getOutput(group, id, pos, enemyPlayer.user.status.level);
            var warCost = gServerMine.getWarCost(group, id, pos);

            if (haveGold < warCost) {
                resp.code = 1; resp.desc = 'not enough gold'; break;
            }
           
            //战斗
            var enemy = new Fighter();
            enemy.initWithPlayer(enemyPlayer, '', true);
            var fighter = new Fighter();
            fighter.initWithPlayer(attackerPlayer, '', true);

            var battleReport = fighter.fight(enemy);
            occupied = battleReport.success;
            
            //判断位置在战斗过程中是否被别人重新占领
            mine = mines[pos];
            if (mine.owner && mine.owner != enemyUid) { 
                resp.data.already = 1; break;
            }

            //战后处理
            var userGold = 0;
            var ownerGold = 0;

            if( occupied ) {
                userGold = warCost; 
                ownerGold = currentOutput - warCost;
                
                gServerMine.addLeave(mine.owner, {gold:ownerGold, enemy:uid, time:attackTime, name:name});
            }else{
                mine.output += warCost;
                userGold = -warCost;
                ownerGold = warCost;
            }

            resp.data.battle = battleReport;
            resp.data.gold = userGold;
            
            var replay = battleReport ? gReplay.addReplay(battleReport) : 0;
            var message = {type: 'server_mine', uid:uid, name:name, time:attackTime, 
                            atkgold:userGold, defgold:ownerGold, succ:occupied, 
                            replay:replay, flag:0, full:gServerMine.isFull(group, id, pos, enemyPlayer.user.status.level)};
            gServerMine.addMessage(mine.owner, message);
        }

        if( occupied ) {
            // 占领

            mine.owner = uid;
            mine.timer = attackTime;
            mine.output = 0;
            mine.name = name;
            mine.headpic = headpic;
            mine.league = req.args.league;
            
            gServerMine.occupying[uid] = {id:id, pos:pos};
        }

        gServerMine.markMineDirty(group, id, pos, mine);

        delete req.args.user;
        resp.data.mine = mine;
        resp.data.occupied = occupied;
    }while(false);

    onReqHandled(res, resp);
}

exports.leave = function(req, res, resp) {
    do {
        var group = getMineGroup(req.args.server);
        var occupying = gServerMine.occupying[req.uid];
        if( !occupying ) {
            resp.code = 1; resp.desc = 'not occupying'; break;
        }
        var id = occupying.id;
        var pos = occupying.pos;
        
        var mine = gServerMine.mines[group][id][pos];
        if(!mine || mine.owner != req.uid) {
            resp.code = 1; resp.desc = 'not occupying'; break;
        }

        var userInfo = gServerMine.users[mine.owner];
        if (userInfo) {
            var gold = gServerMine.getOutput(group, id, pos, userInfo.user.status.level);
        }

        mine.owner = 0;
        mine.timer = 0;
        mine.output = 0;
        mine.name = '';
        mine.headpic = '';
        mine.league = '';

        gServerMine.markMineDirty(group, id, pos, mine);
        gServerMine.removeUser(req.uid);

        resp.data.gold = gold;
        resp.data.mine = mine;
    }while(false);

    gServerMine.save();
    onReqHandled(res, resp);
}

exports.message = function(req, res, resp) {
    resp.nolog = 1;
    resp.data.messages = gServerMine.getMessage(req.uid);
    onReqHandled(res, resp);
}

exports.get_replay = function(req, res, resp) {
    gReplay.getReplay(req.args.id, function(battle){
        if( battle ) {
            resp.data.battle = battle;
        }

        onReqHandled(res, resp);
    });
}

exports.ServerMine = ServerMine;
